package com.android.binderserverclient0.mvi

import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch

open class MviDispatcherKTX<E> : ViewModel(), DefaultLifecycleObserver {

    private var observerCount = 0

    private var version = START_VERSION
    private var currentVersion = START_VERSION

    private val sharedFlow : MutableSharedFlow<ConsumeOnceValue<E>>? by lazy {
        MutableSharedFlow(
            onBufferOverflow =  BufferOverflow.DROP_LATEST,
            extraBufferCapacity = initQueueMaxLength(),
            replay = initQueueMaxLength()
        )
    }

    fun output(activity: AppCompatActivity?, observer: (E) -> Unit) {
        currentVersion = version
        observerCount++
        activity?.lifecycle?.addObserver(this)
        activity?.lifecycleScope?.launch {
            activity.repeatOnLifecycle(Lifecycle.State.STARTED) {
                sharedFlow?.collect {
                    if (version > currentVersion) {
                        if (it.consumeCount >= observerCount) {
                            return@collect
                        }
                        it.consumeCount++
                        observer.invoke(it.value)
                    }
                }
            }
        }
    }

    fun output(fragment: Fragment?, observer: (E) -> Unit) {
        observerCount++
        fragment?.lifecycle?.addObserver(this)
        fragment?.lifecycleScope?.launch {
            fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
                sharedFlow?.collect {
                    if (it.consumeCount >= observerCount) {
                        return@collect
                    }
                    it.consumeCount++
                    observer.invoke(it.value)
                }
            }
        }
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        observerCount--
    }

    suspend fun sendResult(event: E) {
        version++
        sharedFlow?.emit(ConsumeOnceValue(value = event))
    }

    suspend fun input(event: E) {
        onHandle(event)
    }

    protected open suspend fun onHandle(event: E) {
        sendResult(event)
    }

    protected open fun initQueueMaxLength() : Int {
        return DEFAULT_QUEUE_LENGTH
    }

    companion object {
        private const val DEFAULT_QUEUE_LENGTH = 10
        private const val START_VERSION = -1
    }
}